package com.wg.core.thread;

import cn.hutool.core.thread.NamedThreadFactory;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;

import java.util.concurrent.*;

/**
 * 核心线程池
 *
 * @author 少爷123
 */
public class CoreThreadPool implements IThreadPool {
    private static final Log logger = LogFactory.get();
    private static final String LOGIC_NAME = "logic";
    private static final String SCHEDULED_NAME = "scheduled";
    private static final RejectedExecutionHandler DEFAULT_HANDLER = new ThreadPoolExecutor.CallerRunsPolicy();

    /**
     * 逻辑线程池
     */
    private ExecutorService logicThreadPool;

    /**
     * 处理计时器线程池
     */
    private ScheduledThreadPoolExecutor scheduledThreadPool;

    public static CoreThreadPool getInstance() {
        return Singleton.INSTANCE.getManager();
    }

    static {
        CoreThreadPool.getInstance().init();
    }

    @Override
    public void init() {
        buildScheduledThreadPool();
        buildBusinessExecutorService();
    }

    @Override
    public void shutdown() {
        shutdown(scheduledThreadPool, SCHEDULED_NAME);
        shutdown(logicThreadPool, LOGIC_NAME);
    }

    @Override
    public void execute(Runnable task) {
        logicThreadPool.execute(task);
    }

    @Override
    public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long firstIntervalInMs, long intervalInMs) {
        return scheduledThreadPool.scheduleAtFixedRate(task, firstIntervalInMs, intervalInMs, TimeUnit.MILLISECONDS);
    }

    @Override
    public ScheduledFuture<?> schedule(Runnable task, long firstIntervalInMs) {
        return scheduledThreadPool.schedule(task, firstIntervalInMs, TimeUnit.MILLISECONDS);
    }

    private void buildScheduledThreadPool() {
        if (scheduledThreadPool == null) {
            scheduledThreadPool = new ScheduledThreadPoolExecutor(2, new NamedThreadFactory(SCHEDULED_NAME, false),
                    DEFAULT_HANDLER);
            scheduledThreadPool.setRemoveOnCancelPolicy(true);
            scheduledThreadPool.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
        }
    }

    private void buildBusinessExecutorService() {
        if (logicThreadPool == null) {
            int poolSize = Runtime.getRuntime().availableProcessors() * 2;
            logicThreadPool = new ThreadPoolExecutor(poolSize, poolSize, 0, TimeUnit.MILLISECONDS,
                    new LinkedBlockingQueue<>(100_000), new NamedThreadFactory(LOGIC_NAME, false), DEFAULT_HANDLER);
        }
    }

    private void shutdown(ExecutorService executorService, String name) {
        if (executorService == null) {
            return;
        }

        executorService.shutdown();
        try {
            if (!executorService.awaitTermination(2, TimeUnit.MINUTES)) {
                executorService.shutdownNow();
            }
            logger.info("{} thread pool stop service.", name);
        } catch (InterruptedException e) {
            logger.error(e, "Exception occurred while stopping thread pool of {}", name);
            executorService.shutdownNow();
        }
    }

    private enum Singleton {
        INSTANCE;

        CoreThreadPool manager;

        Singleton() {
            this.manager = new CoreThreadPool();
        }

        public CoreThreadPool getManager() {
            return manager;
        }
    }
}
